"""A module that makes it easy to use AppEngine's remote_api.
"""
from __future__ import with_statement
import sys
import os
import getpass

from google.appengine.ext.remote_api import remote_api_stub

import util

APP_NAME = util.getAppConfig()["application"]
os.environ["AUTH_DOMAIN"] = "gmail.com"
os.environ["USER_EMAIL"] = "chris.calo@gmail.com"

REMOTE_API_PATH = "/remote_api"
BATCH_SIZE = 10
SERVER_LOCAL = "localhost:8080"
SERVER_REMOTE = "%s.appspot.com" % APP_NAME

def connect(servername=SERVER_LOCAL):
  """Connects to a remote datastore via AppEngine's Remote API.
  """
  remote_api_stub.ConfigureRemoteDatastore(APP_NAME,
                                           REMOTE_API_PATH,
                                           auth_func,
                                           servername=servername)

def auth_func():
  """Authentication function that fetches the user's username and password
  from the command line.
  """
  email = raw_input("Email: ")
  password = getpass.getpass("Password for %s: " % email)
  return (email, password)

class BatchProcess(object):
  """A batch process that will be executed over the AppEngine Remote API.
  """
  pass

class DatastoreReadBatchProcess(BatchProcess):
  """A batch process that will be executed for all entities of a kind in the
  datastore.
  """
  model = None
  fn = None
  order_by = None
  servername = SERVER_LOCAL
  description = ""
  _timer = util.Timer()
  _count = 0
  
  def __init__(self, model, fn, order_by="__key__",
               servername=SERVER_LOCAL, description=""):
    """
    Arguments:
        model: a google.appengine.ext.db.Model instance
        fn: the function to be called for each batch of entities
        order: the model property that will determine our process order
    """
    self.model = model
    self.fn = fn
    self.order_by = order_by
    self.servername = servername
    self.description = description or self.description
    pass
  
  def connect(self):
    connect(self.servername)
  
  def preamble(self):
    print(self.description)
    print("Batch process in app %s at %s" % (APP_NAME, self.servername))
    print("")
  
  def work_statement(self, entities):
    if entities:
      print("Starting batch process. Processing %i at a time." % BATCH_SIZE)
    else:
      print("No entities to work on. Stopping.")
      exit()
  
  def afterword(self):
    print("")
    print("Finished batch process in app %s at %s" % (APP_NAME, self.servername))
    print("Processed %s entities in %i seconds" % (self._count, self._timer.check().seconds))
  
  def handle_entities(self, entities):
    self.fn(entities)
    self._count = self._count + len(entities)
    if self.order_by == "__key__":
      last_value = entities[-1].key()
    else:
      last_value = getattr(entities[-1], self.order_by)
    sys.stdout.write(".")
    return last_value
  
  def terminate(self):
    print("")
    print("Batch process terminated.")
  
  def execute(self):
    self.preamble()
    try:
      self.connect()
      self._timer.start()
      
      entities = self.get_first_batch()
      self.work_statement(entities)
      
      while entities:
        last_value = self.handle_entities(entities)
        entities = self.get_next_batch(last_value)
    except KeyboardInterrupt:
      self.terminate()
    finally:
      self._timer.stop()
      self.afterword()
  
  def get_first_batch(self):
    query = self.model.gql("ORDER BY %s" % self.order_by)
    return query.fetch(BATCH_SIZE)
  
  def get_next_batch(self, last_value):
    gql = "WHERE %s > :1 ORDER BY %s" % (self.order_by, self.order_by)
    query = self.model.gql(gql, last_value)
    return query.fetch(BATCH_SIZE)
